#!/bin/bash
# SWAP分区耗尽（频繁换页）自动处理脚本
FIX_CODE_ID="swap" 

# 配置参数
SWAP_THRESHOLD=80          # SWAP使用率阈值(%)，超过此值触发处理
PAGE_SWAP_THRESHOLD=100    # 每分钟换页次数阈值，超过此值判定为频繁换页
LOG_FILE="/var/log/swap_management.log"  # 操作日志文件
TEMP_FILE="/tmp/swap_usage.tmp"          # 临时数据文件
SAFE_PROCESSES=("systemd" "sshd" "crond")  # 不允许终止的核心进程
SWAP_INCREMENT=2G          # 扩展SWAP时的增量大小

# 日志函数
log() {
    echo "[$(date +'%Y-%m-%d %H:%M:%S')] $1" >> "$LOG_FILE"
}

# 获取当前SWAP使用率
get_swap_usage() {
    local swap_total=$(free -m | awk '/Swap:/ {print $2}')
    local swap_used=$(free -m | awk '/Swap:/ {print $3}')
    
    if [ $swap_total -eq 0 ]; then
        echo 0
        return
    fi
    
    echo $(( (swap_used * 100) / swap_total ))
}

# 检查换页频率（每分钟）
check_page_swaps() {
    local swap_in=$(vmstat 60 2 | tail -1 | awk '{print $7}')  # 60秒内的换入页数
    local swap_out=$(vmstat 60 2 | tail -1 | awk '{print $8}') # 60秒内的换出页数
    echo $((swap_in + swap_out))
}

# 获取使用SWAP最多的非核心进程
get_top_swap_processes() {
    ps -eo pid,comm,swap --sort=-swap | grep -v "0 kB" | while read -r pid comm swap; do
        # 排除核心进程
        if ! [[ " ${SAFE_PROCESSES[@]} " =~ " ${comm} " ]]; then
            echo "$pid $comm $swap"
        fi
    done | head -n 5  # 取前5个使用SWAP最多的进程
}

# 释放缓存（临时缓解内存压力）
drop_caches() {
    log "尝试释放系统缓存"
    if [ $(id -u) -eq 0 ]; then
        sync  # 先同步数据到磁盘
        echo 1 > /proc/sys/vm/drop_caches  # 释放页缓存
        echo 2 > /proc/sys/vm/drop_caches  # 释放dentries和inodes
        echo 3 > /proc/sys/vm/drop_caches  # 释放页缓存、dentries和inodes
        log "系统缓存已释放"
        return 0
    else
        log "没有权限释放缓存，需要root权限"
        return 1
    fi
}

# 调整SWAP使用策略（降低SWAP优先级）
tune_swap_policy() {
    log "调整SWAP使用策略"
    local current_vfs=$(sysctl vm.vfs_cache_pressure | awk '{print $3}')
    local current_swappiness=$(sysctl vm.swappiness | awk '{print $3}')
    
    # 降低swappiness值（0-100，值越低越倾向使用物理内存）
    if [ $current_swappiness -gt 10 ]; then
        sysctl -w vm.swappiness=10
        log "已将vm.swappiness从$current_swappiness调整为10"
    fi
    
    # 调整缓存压力
    if [ $current_vfs -ne 50 ]; then
        sysctl -w vm.vfs_cache_pressure=50
        log "已将vm.vfs_cache_pressure从$current_vfs调整为50"
    fi
}

# 终止低优先级进程释放内存
terminate_low_prio_processes() {
    log "尝试终止使用SWAP最多的非核心进程"
    local processes=$(get_top_swap_processes)
    
    if [ -z "$processes" ]; then
        log "没有找到可终止的非核心进程"
        return 1
    fi
    
    echo "$processes" | while read -r pid comm swap; do
        log "尝试终止进程: $pid ($comm)，使用SWAP: $swap"
        if kill $pid > /dev/null 2>&1; then
            log "进程$pid ($comm)已终止"
            # 检查终止后SWAP使用是否下降
            sleep 5
            if [ $(get_swap_usage) -lt $SWAP_THRESHOLD ]; then
                log "SWAP使用率已降至阈值以下，停止终止进程"
                return 0
            fi
        else
            log "终止进程$pid ($comm)失败，尝试强制终止"
            kill -9 $pid > /dev/null 2>&1
            sleep 2
        fi
    done
    
    return 0
}

# 扩展SWAP空间（当所有其他方法都失败时）
extend_swap_space() {
    log "尝试扩展SWAP空间"
    
    # 检查是否已有swap文件
    local swap_file="/swapfile"
    if [ -f "$swap_file" ]; then
        log "检测到已有SWAP文件，尝试扩展"
        swapoff $swap_file
        dd if=/dev/zero of=$swap_file bs=1G count=$(echo $SWAP_INCREMENT | sed 's/G//') oflag=append conv=notrunc
        mkswap $swap_file
        swapon $swap_file
    else
        log "创建新的SWAP文件"
        dd if=/dev/zero of=$swap_file bs=1G count=$(echo $SWAP_INCREMENT | sed 's/G//')
        chmod 600 $swap_file
        mkswap $swap_file
        swapon $swap_file
        # 配置开机自动挂载
        echo "$swap_file none swap defaults 0 0" >> /etc/fstab
    fi
    
    log "SWAP空间扩展完成"
    return 0
}

# 主逻辑
main() {
    log "开始SWAP空间检查"
    
    local swap_usage=$(get_swap_usage)
    local page_swaps=$(check_page_swaps)
    
    log "当前SWAP使用率: $swap_usage%，每分钟换页次数: $page_swaps"
    
    # 检查是否达到处理阈值
    if [ $swap_usage -lt $SWAP_THRESHOLD ] && [ $page_swaps -lt $PAGE_SWAP_THRESHOLD ]; then
        log "SWAP使用正常，无需处理"
        exit 0
    fi
    
    log "检测到SWAP使用异常，启动处理流程"
    
    # 第一步：释放缓存
    drop_caches
    
    # 检查是否恢复正常
    if [ $(get_swap_usage) -lt $SWAP_THRESHOLD ]; then
        log "释放缓存后SWAP使用恢复正常"
        exit 0
    fi
    
    # 第二步：调整SWAP使用策略
    tune_swap_policy
    
    # 第三步：终止低优先级进程
    terminate_low_prio_processes
    
    # 再次检查
    if [ $(get_swap_usage) -lt $SWAP_THRESHOLD ]; then
        log "终止进程后SWAP使用恢复正常"
        exit 0
    fi
    
    # 第四步：扩展SWAP空间（最后手段）
    extend_swap_space
    
    # 最终检查
    if [ $(get_swap_usage) -lt $SWAP_THRESHOLD ]; then
        log "扩展SWAP后使用恢复正常"
        exit 0
    else
        log "所有处理措施均已尝试，但SWAP使用仍异常，需要人工干预"
        exit 1
    fi
}

# 执行主逻辑
main
    